<!DOCTYPE html>



  


<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />



  <meta name="google-site-verification" content="Xyy9fg5uzmq0Ysc9cQNgP4xPutZF0sBaJsjSHK55zC0" />



  <meta name="msvalidate.01" content="914C9C1CBC7078C5E2AC612AD5C1CC6C" />












  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.3" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.3">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.3">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.3">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.3" color="#222">





  <meta name="keywords" content="Dapp入门,以太坊概念," />





  <link rel="alternate" href="/atom.xml" title="深入浅出区块链" type="application/atom+xml" />






<meta name="description" content="今天我们来编写一个完整的去中心化（区块链）应用（Dapps）, 本文可以和编写智能合约结合起来看。">
<meta name="keywords" content="Dapp入门,以太坊概念">
<meta property="og:type" content="article">
<meta property="og:title" content="一步步教你开发、部署第一个去中心化应用(Dapp) - 宠物商店">
<meta property="og:url" content="https://learnblockchain.cn/2018/01/12/first-dapp/index.html">
<meta property="og:site_name" content="深入浅出区块链">
<meta property="og:description" content="今天我们来编写一个完整的去中心化（区块链）应用（Dapps）, 本文可以和编写智能合约结合起来看。">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://learnblockchain.cn/images/Petshop.jpg">
<meta property="og:image" content="https://learnblockchain.cn/images/ganache-initial.png">
<meta property="og:image" content="https://learnblockchain.cn/images/ganache-migrated.png">
<meta property="og:image" content="https://learnblockchain.cn/images/metamask-initial.png">
<meta property="og:image" content="https://learnblockchain.cn/images/metamask-seed.png">
<meta property="og:image" content="https://learnblockchain.cn/images/metamask-account1.png">
<meta property="og:image" content="https://learnblockchain.cn/images/metamask-transactionconfirm.png">
<meta property="og:image" content="https://learnblockchain.cn/images/metamask-transactionsuccess.png">
<meta property="og:updated_time" content="2018-01-24T12:39:59.700Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="一步步教你开发、部署第一个去中心化应用(Dapp) - 宠物商店">
<meta name="twitter:description" content="今天我们来编写一个完整的去中心化（区块链）应用（Dapps）, 本文可以和编写智能合约结合起来看。">
<meta name="twitter:image" content="https://learnblockchain.cn/images/Petshop.jpg">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Pisces',
    version: '5.1.3',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":true,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: 'UT2K1ZRSWK',
      apiKey: '0bbb261dfad58df8f8a657a1ed3255cd',
      indexName: 'learnblockchain',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://learnblockchain.cn/2018/01/12/first-dapp/"/>





  <title>一步步教你开发、部署第一个去中心化应用(Dapp) - 宠物商店 | 深入浅出区块链</title>
  








  <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
    (adsbygoogle = window.adsbygoogle || []).push({
        google_ad_client: "ca-pub-6273382609469378",
        enable_page_level_ads: true
    });
</script>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">深入浅出区块链</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <h1 class="site-subtitle" itemprop="description">系统学习区块链技术</h1>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-wiki">
          <a href="https://wiki.learnblockchain.cn" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-book"></i> <br />
            
            Wiki
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  
  <div class="algolia-popup popup search-popup">
    <div class="algolia-search">
      <div class="algolia-search-input-icon">
        <i class="fa fa-search"></i>
      </div>
      <div class="algolia-search-input" id="algolia-search-input"></div>
    </div>

    <div class="algolia-results">
      <div id="algolia-stats"></div>
      <div id="algolia-hits"></div>
      <div id="algolia-pagination" class="algolia-pagination"></div>
    </div>

    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
  </div>




    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://learnblockchain.cn/2018/01/12/first-dapp/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Tiny熊">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="深入浅出区块链">
    </span>

    
      <header class="post-header">

        
        
          <h2 class="post-title" itemprop="name headline">一步步教你开发、部署第一个去中心化应用(Dapp) - 宠物商店</h2>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-01-12T22:36:39+08:00">
                2018-01-12
              </time>
            

            
              <span class="post-meta-divider">|</span>
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-check-o"></i>
              </span>
              
                <span class="post-meta-item-text">更新于&#58;</span>
              
              <time title="更新于" itemprop="dateModified" datetime="2018-01-24T20:39:59+08:00">
                2018-01-24
              </time>
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/ethereum/" itemprop="url" rel="index">
                    <span itemprop="name">以太坊</span>
                  </a>
                </span>

                
                
                  ， 
                
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/ethereum/Dapp/" itemprop="url" rel="index">
                    <span itemprop="name">Dapp</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>今天我们来编写一个完整的去中心化（区块链）应用（Dapps）, 本文可以和<a href="https://learnblockchain.cn/2017/11/24/init-env/">编写智能合约</a>结合起来看。</p>
<a id="more"></a>
<h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>阅读本文前，你应该对以太坊、智能合约有所了解，如果你还不了解，建议你先看<a href="https://learnblockchain.cn/2017/11/20/whatiseth/">以太坊是什么</a><br>除此之外，你最好还了解一些HTML及JavaScript知识。</p>
<p>本文通过实例教大家来开发去中心化应用，应用效果如图：<br><img src="https://learnblockchain.cn/images/Petshop.jpg" alt=""></p>
<p>从本文，你可以学习到：</p>
<ul>
<li>搭建智能合约开发环境</li>
<li>创建Truffle项目</li>
<li>编写智能合约</li>
<li>编译和部署智能合约到区块链</li>
<li>如何通过Web3和智能合约交互</li>
<li>MetaMask 的使用</li>
</ul>
<p><a href="https://xiaozhuanlan.com/blockchaincore" target="_blank" rel="noopener">小专栏</a>用户在教程结尾处可以下载完整的Dapp代码。</p>
<h2 id="项目背景"><a href="#项目背景" class="headerlink" title="项目背景"></a>项目背景</h2><p>Pete有一个宠物店，有16只宠物，他想开发一个去中心化应用，让大家来领养宠物。<br>在truffle box中，已经提供了pet-shop的网站部分的代码，我们只需要编写合约及交互部分。</p>
<h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><ol>
<li>安装<a href="https://nodejs.org/en/download/" target="_blank" rel="noopener">Node</a></li>
<li>安装 Truffle ：<code>npm install -g truffle</code></li>
<li>安装<a href="http://truffleframework.com/ganache/" target="_blank" rel="noopener">Ganache</a></li>
</ol>
<blockquote>
<p>Ganache（或Ganache CLI）已经取代了 testrpc。</p>
</blockquote>
<h2 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h2><ol>
<li><p>建立项目目录并进入</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;</span><span class="bash"> mkdir pet-shop-tutorial</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> <span class="built_in">cd</span> pet-shop-tutorial</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>使用truffle unbox 创建项目</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"> &gt;</span><span class="bash"> truffle unbox pet-shop</span></span><br><span class="line"> Downloading...</span><br><span class="line"> Unpacking...</span><br><span class="line"> Setting up...</span><br><span class="line"> Unbox successful. Sweet!</span><br><span class="line"></span><br><span class="line">Commands:</span><br><span class="line"></span><br><span class="line">  Compile:        truffle compile</span><br><span class="line">  Migrate:        truffle migrate</span><br><span class="line">  Test contracts: truffle test</span><br><span class="line">  Run dev server: npm run dev</span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>这一步需要等待一会</p>
<blockquote>
<p>也可以使用truffle init 来创建一个全新的项目。</p>
</blockquote>
<h2 id="项目目录结构"><a href="#项目目录结构" class="headerlink" title="项目目录结构"></a>项目目录结构</h2><p><code>contracts/</code>  智能合约的文件夹，所有的智能合约文件都放置在这里，里面包含一个重要的合约Migrations.sol（稍后再讲）<br><code>migrations/</code> 用来处理部署（迁移）智能合约 ，迁移是一个额外特别的合约用来保存合约的变化。<br><code>test/</code> 智能合约测试用例文件夹<br><code>truffle.js/</code> 配置文件</p>
<p>其他代码可以暂时不用管</p>
<h2 id="编写智能合约"><a href="#编写智能合约" class="headerlink" title="编写智能合约"></a>编写智能合约</h2><p>智能合约承担着分布式应用的后台逻辑和存储。智能合约使用solidity编写，可阅读<br><a href="https://learnblockchain.cn/categories/ethereum/Solidity/">solidity系列文章</a></p>
<p>在contracts目录下，添加合约文件Adoption.sol<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">pragma solidity ^<span class="number">0.4</span><span class="number">.17</span>;</span><br><span class="line"></span><br><span class="line">contract Adoption &#123;</span><br><span class="line"></span><br><span class="line">  address[<span class="number">16</span>] public adopters;  <span class="comment">// 保存领养者的地址</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 领养宠物</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">adopt</span>(<span class="params">uint petId</span>) <span class="title">public</span> <span class="title">returns</span> (<span class="params">uint</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">require</span>(petId &gt;= <span class="number">0</span> &amp;&amp; petId &lt;= <span class="number">15</span>);  <span class="comment">// 确保id在数组长度内</span></span><br><span class="line"></span><br><span class="line">    adopters[petId] = msg.sender;        <span class="comment">// 保存调用这地址 </span></span><br><span class="line">    <span class="keyword">return</span> petId;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 返回领养者</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">getAdopters</span>(<span class="params"></span>) <span class="title">public</span> <span class="title">view</span> <span class="title">returns</span> (<span class="params">address[<span class="number">16</span>]</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> adopters;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="编译部署智能合约"><a href="#编译部署智能合约" class="headerlink" title="编译部署智能合约"></a>编译部署智能合约</h2><p>Truffle集成了一个开发者控制台，可用来生成一个开发链用来测试和部署智能合约。</p>
<h3 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h3><p>Solidity是编译型语言，需要把可读的Solidity代码编译为EVM字节码才能运行。<br>dapp的根目录pet-shop-tutorial下，<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;</span><span class="bash"> truffle compile</span></span><br></pre></td></tr></table></figure></p>
<p>输出<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Compiling ./contracts/Adoption.sol...</span><br><span class="line">Writing artifacts to ./build/contracts</span><br></pre></td></tr></table></figure></p>
<h3 id="部署"><a href="#部署" class="headerlink" title="部署"></a>部署</h3><p>编译之后，就可以部署到区块链上。<br>在migrations文件夹下已经有一个1_initial_migration.js部署脚本，用来部署Migrations.sol合约。<br>Migrations.sol 用来确保不会部署相同的合约。</p>
<p>现在我们来创建一个自己的部署脚本<code>2_deploy_contracts.js</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var Adoption = artifacts.require(&quot;Adoption&quot;);</span><br><span class="line"></span><br><span class="line">module.exports = function(deployer) &#123;</span><br><span class="line">  deployer.deploy(Adoption);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>在执行部署之前，需要确保有一个区块链运行, 可以使用<br><a href="http://truffleframework.com/ganache/" target="_blank" rel="noopener">Ganache</a>来开启一个私链来进行开发测试，默认会在7545端口上运行一个开发链。<br>Ganache 启动之后是这样：<br><img src="https://learnblockchain.cn/images/ganache-initial.png" alt=""></p>
<p>接下来执行部署命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&gt; truffle  migrate</span><br></pre></td></tr></table></figure></p>
<p>执行后，有一下类似的输出，<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">Using network &apos;develop&apos;.</span><br><span class="line"></span><br><span class="line">Running migration: 1_initial_migration.js</span><br><span class="line">  Deploying Migrations...</span><br><span class="line">  ... 0x3076b7dac65afc44ec51508bf6f2b6894f833f0f9560ecad2d6d41ed98a4679f</span><br><span class="line">  Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0</span><br><span class="line">Saving successful migration to network...</span><br><span class="line">  ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956</span><br><span class="line">Saving artifacts...</span><br><span class="line">Running migration: 2_deploy_contracts.js</span><br><span class="line">  Deploying Adoption...</span><br><span class="line">  ... 0x2c6ab4471c225b5473f2079ee42ca1356007e51d5bb57eb80bfeb406acc35cd4</span><br><span class="line">  Adoption: 0x345ca3e014aaf5dca488057592ee47305d9b3e10</span><br><span class="line">Saving successful migration to network...</span><br><span class="line">  ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0</span><br><span class="line">Saving artifacts...</span><br></pre></td></tr></table></figure></p>
<p>在打开的Ganache里可以看到区块链状态的变化，现在产生了4个区块。<br><img src="https://learnblockchain.cn/images/ganache-migrated.png" alt=""><br>这时说明已经智能合约已经部署好了。</p>
<h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>现在我们来测试一下智能合约，测试用例可以用 JavaScript or Solidity来编写，这里使用Solidity。</p>
<p>在<code>test</code>目录下新建一个<code>TestAdoption.sol</code>，编写测试合约<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">pragma solidity ^<span class="number">0.4</span><span class="number">.17</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"truffle/Assert.sol"</span>;   <span class="comment">// 引入的断言</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"truffle/DeployedAddresses.sol"</span>;  <span class="comment">// 用来获取被测试合约的地址</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"../contracts/Adoption.sol"</span>;      <span class="comment">// 被测试合约</span></span><br><span class="line"></span><br><span class="line">contract TestAdoption &#123;</span><br><span class="line">  Adoption adoption = Adoption(DeployedAddresses.Adoption());</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 领养测试用例</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">testUserCanAdoptPet</span>(<span class="params"></span>) <span class="title">public</span> </span>&#123;</span><br><span class="line">    uint returnedId = adoption.adopt(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">    uint expected = <span class="number">8</span>;</span><br><span class="line">    Assert.equal(returnedId, expected, <span class="string">"Adoption of pet ID 8 should be recorded."</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 宠物所有者测试用例</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">testGetAdopterAddressByPetId</span>(<span class="params"></span>) <span class="title">public</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 期望领养者的地址就是本合约地址，因为交易是由测试合约发起交易，</span></span><br><span class="line">    address expected = <span class="keyword">this</span>;</span><br><span class="line">    address adopter = adoption.adopters(<span class="number">8</span>);</span><br><span class="line">    Assert.equal(adopter, expected, <span class="string">"Owner of pet ID 8 should be recorded."</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试所有领养者</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">testGetAdopterAddressByPetIdInArray</span>(<span class="params"></span>) <span class="title">public</span> </span>&#123;</span><br><span class="line">  <span class="comment">// 领养者的地址就是本合约地址</span></span><br><span class="line">    address expected = <span class="keyword">this</span>;</span><br><span class="line">    address[<span class="number">16</span>] memory adopters = adoption.getAdopters();</span><br><span class="line">    Assert.equal(adopters[<span class="number">8</span>], expected, <span class="string">"Owner of pet ID 8 should be recorded."</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>Assert.sol 及 DeployedAddresses.sol是Truffle框架提供，在test目录下并不提供truffle目录。</p>
<p>TestAdoption合约中添加adopt的测试用例</p>
<h3 id="运行测试用例"><a href="#运行测试用例" class="headerlink" title="运行测试用例"></a>运行测试用例</h3><p>在终端中，执行<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">truffle <span class="built_in">test</span></span><br></pre></td></tr></table></figure></p>
<p>如果测试通过，则终端输出：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Using network &apos;develop&apos;.</span><br><span class="line"></span><br><span class="line">Compiling ./contracts/Adoption.sol...</span><br><span class="line">Compiling ./test/TestAdoption.sol...</span><br><span class="line">Compiling truffle/Assert.sol...</span><br><span class="line">Compiling truffle/DeployedAddresses.sol...</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  TestAdoption</span><br><span class="line">    ✓ testUserCanAdoptPet (62ms)</span><br><span class="line">    ✓ testGetAdopterAddressByPetId (53ms)</span><br><span class="line">    ✓ testGetAdopterAddressByPetIdInArray (73ms)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  3 passing (554ms)</span><br></pre></td></tr></table></figure></p>
<h2 id="创建用户接口和智能合约交互"><a href="#创建用户接口和智能合约交互" class="headerlink" title="创建用户接口和智能合约交互"></a>创建用户接口和智能合约交互</h2><p>我们已经编写和部署及测试好了我们的合约，接下我们为合约编写UI，让合约真正可以用起来。</p>
<p>在Truffle Box <code>pet-shop</code>里，已经包含了应用的前端代码，代码在<code>src/</code>文件夹下。</p>
<p>在编辑器中打开<code>src/js/app.js</code><br>可以看到用来管理整个应用的App对象，init函数加载宠物信息，就初始化<a href="https://github.com/ethereum/web3.js/" target="_blank" rel="noopener">web3</a>.<br>web3是一个实现了与以太坊节点通信的库，我们利用web3来和合约进行交互。</p>
<h3 id="初始化web3"><a href="#初始化web3" class="headerlink" title="初始化web3"></a>初始化web3</h3><p>接下来，我们来编辑app.js修改initWeb3():<br>删除注释，修改为：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">initWeb3: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Is there an injected web3 instance?</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> web3 !== <span class="string">'undefined'</span>) &#123;</span><br><span class="line">    App.web3Provider = web3.currentProvider;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// If no injected web3 instance is detected, fall back to Ganache</span></span><br><span class="line">    App.web3Provider = <span class="keyword">new</span> Web3.providers.HttpProvider(<span class="string">'http://localhost:7545'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  web3 = <span class="keyword">new</span> Web3(App.web3Provider);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> App.initContract();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>代码中优先使用<a href="https://github.com/ethereum/mist" target="_blank" rel="noopener">Mist</a> 或 <a href="https://metamask.io/" target="_blank" rel="noopener">MetaMask</a>提供的web3实例，如果没有则从本地环境创建一个。</p>
<h3 id="实例化合约"><a href="#实例化合约" class="headerlink" title="实例化合约"></a>实例化合约</h3><p>使用truffle-contract会帮我们保存合约部署的信息，就不需要我们手动修改合约地址，修改initContract()代码如下：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">initContract: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 加载Adoption.json，保存了Adoption的ABI（接口说明）信息及部署后的网络(地址)信息，它在编译合约的时候生成ABI，在部署的时候追加网络信息</span></span><br><span class="line">  $.getJSON(<span class="string">'Adoption.json'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 用Adoption.json数据创建一个可交互的TruffleContract合约实例。</span></span><br><span class="line">    <span class="keyword">var</span> AdoptionArtifact = data;</span><br><span class="line">    App.contracts.Adoption = TruffleContract(AdoptionArtifact);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Set the provider for our contract</span></span><br><span class="line">    App.contracts.Adoption.setProvider(App.web3Provider);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Use our contract to retrieve and mark the adopted pets</span></span><br><span class="line">    <span class="keyword">return</span> App.markAdopted();</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> App.bindEvents();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="处理领养"><a href="#处理领养" class="headerlink" title="处理领养"></a>处理领养</h3><p>修改markAdopted()代码：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">markAdopted: <span class="function"><span class="keyword">function</span>(<span class="params">adopters, account</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> adoptionInstance;</span><br><span class="line"></span><br><span class="line">  App.contracts.Adoption.deployed().then(<span class="function"><span class="keyword">function</span>(<span class="params">instance</span>) </span>&#123;</span><br><span class="line">    adoptionInstance = instance;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用合约的getAdopters(), 用call读取信息不用消耗gas</span></span><br><span class="line">    <span class="keyword">return</span> adoptionInstance.getAdopters.call();</span><br><span class="line">  &#125;).then(<span class="function"><span class="keyword">function</span>(<span class="params">adopters</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; adopters.length; i++) &#123;</span><br><span class="line">      <span class="keyword">if</span> (adopters[i] !== <span class="string">'0x0000000000000000000000000000000000000000'</span>) &#123;</span><br><span class="line">        $(<span class="string">'.panel-pet'</span>).eq(i).find(<span class="string">'button'</span>).text(<span class="string">'Success'</span>).attr(<span class="string">'disabled'</span>, <span class="literal">true</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;).catch(<span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(err.message);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>修改handleAdopt()代码：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">handleAdopt: <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">  event.preventDefault();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> petId = <span class="built_in">parseInt</span>($(event.target).data(<span class="string">'id'</span>));</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> adoptionInstance;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 获取用户账号</span></span><br><span class="line">  web3.eth.getAccounts(<span class="function"><span class="keyword">function</span>(<span class="params">error, accounts</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error);</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">var</span> account = accounts[<span class="number">0</span>];</span><br><span class="line">  </span><br><span class="line">    App.contracts.Adoption.deployed().then(<span class="function"><span class="keyword">function</span>(<span class="params">instance</span>) </span>&#123;</span><br><span class="line">      adoptionInstance = instance;</span><br><span class="line">  </span><br><span class="line">      <span class="comment">// 发送交易领养宠物</span></span><br><span class="line">      <span class="keyword">return</span> adoptionInstance.adopt(petId, &#123;<span class="attr">from</span>: account&#125;);</span><br><span class="line">    &#125;).then(<span class="function"><span class="keyword">function</span>(<span class="params">result</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> App.markAdopted();</span><br><span class="line">    &#125;).catch(<span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(err.message);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="在浏览器中运行"><a href="#在浏览器中运行" class="headerlink" title="在浏览器中运行"></a>在浏览器中运行</h2><h3 id="安装-MetaMask"><a href="#安装-MetaMask" class="headerlink" title="安装 MetaMask"></a>安装 MetaMask</h3><p>MetaMask 是一款插件形式的以太坊轻客户端，开发过程中使用MetaMask和我们的dapp进行交互是个很好的选择，通过此<a href="https://metamask.io/" target="_blank" rel="noopener">链接</a>安装，安装完成后，浏览器工具条会显示一个小狐狸图标。</p>
<h3 id="配置钱包"><a href="#配置钱包" class="headerlink" title="配置钱包"></a>配置钱包</h3><p>在接受隐私说明后，会出现页面如下：<br><img src="https://learnblockchain.cn/images/metamask-initial.png" alt=""></p>
<p>这里我们通过还原一个Ganache为我们创建好的钱包，作为我们的开发测试钱包。点击页面的<strong> Import Existing DEN</strong>，输入Ganache显示的助记词。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">candy maple cake sugar pudding cream honey rich smooth crumble sweet treat</span><br></pre></td></tr></table></figure></p>
<p>然后自己想要的密码，点击OK。<br>如图：<br><img src="https://learnblockchain.cn/images/metamask-seed.png" alt=""></p>
<h3 id="连接开发区块链网络"><a href="#连接开发区块链网络" class="headerlink" title="连接开发区块链网络"></a>连接开发区块链网络</h3><p>默认连接的是以太坊主网（左上角显示），选择<strong>Custom RPC</strong>，添加一个网络：<strong><a href="http://127.0.0.1:7545" target="_blank" rel="noopener">http://127.0.0.1:7545</a></strong>，点返回后，显示如下：<br><img src="/images/metamask-account1.png" alt=""><br>这是左上角显示为<strong>Private Network</strong>，账号是Ganache中默认的第一个账号。</p>
<p>至此MetaMask的安装，配置已经完成。</p>
<h3 id="安装和配置lite-server"><a href="#安装和配置lite-server" class="headerlink" title="安装和配置lite-server"></a>安装和配置lite-server</h3><p>接下来需要本地的web 服务器提供服务的访问， Truffle Box <strong>pet-shop</strong>里提供了一个<strong>lite-server</strong>可以直接使用，我们看看它是如何工作的。<br><strong>bs-config.json</strong>指示了lite-server的工作目录。<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"server"</span>: &#123;</span><br><span class="line">    <span class="attr">"baseDir"</span>: [<span class="string">"./src"</span>, <span class="string">"./build/contracts"</span>]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>./src 是网站文件目录<br>./build/contracts 是合约输出目录</p>
<p>以此同时，在package.json文件的scripts中添加了dev命令：<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">"scripts": &#123;</span><br><span class="line">  "dev": "lite-server",</span><br><span class="line">  "test": "echo \"Error: no test specified\" &amp;&amp; exit 1"</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure></p>
<p>当运行npm run dev的时候，就会启动lite-server</p>
<h3 id="启动服务"><a href="#启动服务" class="headerlink" title="启动服务"></a>启动服务</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&gt; npm run dev</span><br></pre></td></tr></table></figure>
<p>会自动打开浏览器显示我们的dapp，如本文的第一张图。<br>现在领养一直宠物看看，当我们点击<strong>Adopt</strong>时，MetaMask会提示我们交易的确认，如图：</p>
<p><img src="https://learnblockchain.cn/images/metamask-transactionconfirm.png" alt=""></p>
<p>点击Submit确认后，就可以看到成功领养了这次宠物。</p>
<p>在MetaMask中，也可以看到交易的清单：<br><img src="https://learnblockchain.cn/images/metamask-transactionsuccess.png" alt=""></p>
<p>好了，恭喜你，即将成为一名去中心化式应用开发者的你已经成为迈出了坚实的一步。<br>如果学习中遇到问题，欢迎来我的<strong><a href="https://t.xiaomiquan.com/RfAu7uj" target="_blank" rel="noopener">知识星球</a></strong>交流。</p>
<h2 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h2><ul>
<li><a href="http://truffleframework.com/tutorials/pet-shop" target="_blank" rel="noopener">Truffle手册</a></li>
</ul>
<p><a href="https://learnblockchain.cn/">深入浅出区块链</a> - 系统学习区块链，打造最好的区块链技术博客。</p>

      
    </div>
    
    
    

    
      <div>
        <div id="wechat_subscriber" style="display: block; padding: 10px 0; margin: 20px auto; width: 100%; text-align: center">
    <img id="wechat_subscriber_qcode" src="/images/qrcode.jpg" alt="Tiny熊 wechat" style="width: 200px; max-width: 100%;"/>
    <div>微信号：深入浅出区块链技术，欢迎订阅</div>
</div>

      </div>
    

    
      <div>
        <div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
  <div>有收获，赞赏下</div>
  <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
    <span>打赏</span>
  </button>
  <div id="QR" style="display: none;">

    
      <div id="wechat" style="display: inline-block">
        <img id="wechat_qr" src="/images/qr_pay.jpg" alt="Tiny熊 微信支付"/>
        <p>微信支付</p>
      </div>
    

    

    

  </div>
</div>

      </div>
    

    
      <div>
        <ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者：</strong>
    Tiny熊
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://learnblockchain.cn/2018/01/12/first-dapp/" title="一步步教你开发、部署第一个去中心化应用(Dapp) - 宠物商店">https://learnblockchain.cn/2018/01/12/first-dapp/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>
    本文采用 <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" rel="external nofollow" target="_blank">CC BY-NC-SA 3.0</a> 许可协议。转载请注明出处！
  </li>
</ul>

      </div>
    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Dapp入门/" rel="tag"># Dapp入门</a>
          
            <a href="/tags/以太坊概念/" rel="tag"># 以太坊概念</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2018/01/11/guide/" rel="next" title="区块链技术学习指引">
                <i class="fa fa-chevron-left"></i> 区块链技术学习指引
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2018/01/12/create_token/" rel="prev" title="一步步教你创建自己的数字货币（代币）进行ICO">
                一步步教你创建自己的数字货币（代币）进行ICO <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">Tiny熊</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">40</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">12</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">51</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          <div class="links-of-author motion-element">
            
              
                <span class="links-of-author-item">
                  <a href="https://github.com/xilibi2003" target="_blank" title="GitHub">
                    
                      <i class="fa fa-fw fa-github"></i>GitHub</a>
                </span>
              
                <span class="links-of-author-item">
                  <a href="http://weibo.com/xilibi2003" target="_blank" title="Weibo">
                    
                      <i class="fa fa-fw fa-weibo"></i>Weibo</a>
                </span>
              
                <span class="links-of-author-item">
                  <a href="https://www.zhihu.com/people/xiong-li-bing/activities" target="_blank" title="ZhiHu">
                    
                      <i class="fa fa-fw fa-globe"></i>ZhiHu</a>
                </span>
              
                <span class="links-of-author-item">
                  <a href="mailto:xilibi2003@gmail.com" target="_blank" title="E-Mail">
                    
                      <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                </span>
              
            
          </div>

          
          

          
          
            <div class="links-of-blogroll motion-element links-of-blogroll-block">
              <div class="links-of-blogroll-title">
                <i class="fa  fa-fw fa-link"></i>
                友链
              </div>
              <ul class="links-of-blogroll-list">
                
                  <li class="links-of-blogroll-item">
                    <a href="https://xiaozhuanlan.com/blockchaincore" title="区块链技术专栏" target="_blank">区块链技术专栏</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://t.xiaomiquan.com/RfAu7uj" title="技术交流-知识星球" target="_blank">技术交流-知识星球</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://bitshuo.com" title="区块链技术中文社区" target="_blank">区块链技术中文社区</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://news.tangwen.org" title="汤问资讯" target="_blank">汤问资讯</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.bitask.org" title="币问" target="_blank">币问</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://www.hubwiz.com/course?affid＝lbc" title="汇智网互动课程中心" target="_blank">汇智网互动课程中心</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://hiblock.net/" title="HiBlock区块链社区" target="_blank">HiBlock区块链社区</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://eosfans.io/" title="EOS中文社区" target="_blank">EOS中文社区</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://blockflow.net/" title="BlockFlow" target="_blank">BlockFlow</a>
                  </li>
                
              </ul>
            </div>
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#写在前面"><span class="nav-number">1.</span> <span class="nav-text">写在前面</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#项目背景"><span class="nav-number">2.</span> <span class="nav-text">项目背景</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#环境搭建"><span class="nav-number">3.</span> <span class="nav-text">环境搭建</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#创建项目"><span class="nav-number">4.</span> <span class="nav-text">创建项目</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#项目目录结构"><span class="nav-number">5.</span> <span class="nav-text">项目目录结构</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#编写智能合约"><span class="nav-number">6.</span> <span class="nav-text">编写智能合约</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#编译部署智能合约"><span class="nav-number">7.</span> <span class="nav-text">编译部署智能合约</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#编译"><span class="nav-number">7.1.</span> <span class="nav-text">编译</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#部署"><span class="nav-number">7.2.</span> <span class="nav-text">部署</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#测试"><span class="nav-number">8.</span> <span class="nav-text">测试</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#运行测试用例"><span class="nav-number">8.1.</span> <span class="nav-text">运行测试用例</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#创建用户接口和智能合约交互"><span class="nav-number">9.</span> <span class="nav-text">创建用户接口和智能合约交互</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#初始化web3"><span class="nav-number">9.1.</span> <span class="nav-text">初始化web3</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#实例化合约"><span class="nav-number">9.2.</span> <span class="nav-text">实例化合约</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#处理领养"><span class="nav-number">9.3.</span> <span class="nav-text">处理领养</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#在浏览器中运行"><span class="nav-number">10.</span> <span class="nav-text">在浏览器中运行</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#安装-MetaMask"><span class="nav-number">10.1.</span> <span class="nav-text">安装 MetaMask</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#配置钱包"><span class="nav-number">10.2.</span> <span class="nav-text">配置钱包</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#连接开发区块链网络"><span class="nav-number">10.3.</span> <span class="nav-text">连接开发区块链网络</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#安装和配置lite-server"><span class="nav-number">10.4.</span> <span class="nav-text">安装和配置lite-server</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#启动服务"><span class="nav-number">10.5.</span> <span class="nav-text">启动服务</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#参考文档"><span class="nav-number">11.</span> <span class="nav-text">参考文档</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      
        <div class="back-to-top">
          <i class="fa fa-arrow-up"></i>
          
        </div>
      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2018</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Tiny熊</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Pisces</a></div>



  <div class="footer-custom"><a target="_blank" href="http://www.miitbeian.gov.cn/">粤ICP备17140514号-1</a></div>


        







  <div style="display: none;">
    <script src="//s95.cnzz.com/z_stat.php?id=1265946080&web_id=1265946080" language="JavaScript"></script>
  </div>



        
      </div>
    </footer>

    

    
      <div id="needsharebutton-float">
        <span class="btn">
          <i class="fa fa-share-alt" aria-hidden="true"></i>
        </span>
      </div>
    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.3"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.3"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.3"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.3"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.3"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.3"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.3"></script>



  


  




	





  





  












  




  
  
  
  <link rel="stylesheet" href="/lib/algolia-instant-search/instantsearch.min.css">

  
  
  <script src="/lib/algolia-instant-search/instantsearch.min.js"></script>
  

  <script src="/js/src/algolia-search.js?v=5.1.3"></script>



  

  

  
<script>
(function(){
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
    }
    else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>


  
  
  
  <link rel="stylesheet" href="/lib/needsharebutton/needsharebutton.css">

  
  
  <script src="/lib/needsharebutton/needsharebutton.js"></script>

  <script>
    
    
      flOptions = {};
      
          flOptions.iconStyle = "default";
      
          flOptions.boxForm = "horizontal";
      
          flOptions.position = "middleRight";
      
          flOptions.networks = "Weibo,Wechat,Twitter,Facebook";
      
      new needShareButton('#needsharebutton-float', flOptions);
    
  </script>

  

  

  

  

</body>
</html>
